#include "VNDTry.hpp"
#include <cstdlib>

void    VNDTry::execute(const Solution& inSolution, unsigned inMaxIt) EXC_THROWER()
{
    EXC_RETHROW_BEGIN()
    {
        unsigned        ir_rand;
        unsigned        it_rand;
        unsigned        iteration = 0;
        unsigned        amelioration = 0;
        unsigned        last_amelioration_iteration = 0;
        SolutionEval    soleval;
        SolutionEval    solevalbest;
        
        std::multimap<unsigned, unsigned>               tournees_by_size;
        std::multimap<unsigned, unsigned>::iterator         it;
        std::multimap<unsigned, unsigned>::reverse_iterator ir;
        std::pair<TourneeEvalResult, TourneeEvalResult> tr_eval;
        std::pair<TourneeEvalResult, TourneeEvalResult> tr_eval_n;
        std::pair<unsigned, unsigned>   tr;
        
        vtBestSolution = inSolution;
        vtSolution = vtBestSolution;
        //std::cout << vtBestSolution.toString() << std::endl;
        vtBestSolution.eval(&solevalbest);
        //std::cout << solevalbest.toString() << std::endl;
        
        for (;; iteration++)
        {
            //vtSolution = vtBestSolution;
            
            //std::cout << vtSolution.toString() << std::endl;
            
            // prendre deux tournees aleatoirement
            // la methode garantit que les tournees sont differentes
            // et remplies
            
            vtSolution.buildTourneeBySize(tournees_by_size);
            
            do
            {
                ir = tournees_by_size.rbegin();
                ir_rand = rand() % (tournees_by_size.size());
                
                for (unsigned i = 0 ; i < ir_rand ; i++)
                    ++ir;
                    
                it = tournees_by_size.begin();
                it_rand = rand() % (tournees_by_size.size());
                
                for (unsigned i = 0 ; i < it_rand ; i++)
                    ++it;
            }   while (it->second == ir->second);
            
            Tournee&    left = vtSolution[it->second];
            Tournee&    right = vtSolution[ir->second];
            
            //std::cout << "left : " << left.toString() << std::endl;
            //std::cout << "right : " << right.toString() << std::endl;
            
            // eval de depart
            left.eval(&tr_eval.first);
            right.eval(&tr_eval.second);
            
            // tenter le basic randomOROpt, meilleur compromis alea/amelioration
            basicRandomOrOpt(left, right);
            
            // eval de fin
            if (left.getNbClients() == 0)
            {
                if (right.getNbClients() == 0)
                {
                    tr_eval_n.first.erValable = true;
                    tr_eval_n.second.erValable = true;
                }
                else
                {
                    tr_eval_n.first.erValable = true;
                    tr_eval_n.first.erTotalTime = 0;
                    right.eval(&tr_eval_n.second);
                }
            }
            else
            {
                if (right.getNbClients() == 0)
                {
                    tr_eval_n.second.erValable = true;
                    tr_eval_n.second.erTotalTime = 0;
                    left.eval(&tr_eval_n.first);
                }
                else
                {
                    right.eval(&tr_eval_n.second);
                    left.eval(&tr_eval_n.first);
                }
            }
            
            // ne pas verifier que la distance est minimisee
            // pour rajouter de l'alea
            if ((tr_eval_n.first.erValable && tr_eval_n.second.erValable))
            {
                vtSolution.validateTournee(it->second);
                vtSolution.validateTournee(ir->second);
            }
            else
            {
                // echec basic randomOROpt
                // on tente le basic Opt2*
                vtSolution.restoreTournee(ir->second);
                vtSolution.restoreTournee(it->second);
                
                random2Opt(right, left);
                left.eval(&tr_eval_n.first);
                right.eval(&tr_eval_n.second);
                
                if ((tr_eval_n.first.erValable && tr_eval_n.second.erValable) && 
                    ((tr_eval_n.first.erTotalTime + tr_eval_n.second.erTotalTime)
                        <= (tr_eval.first.erTotalTime + tr_eval.second.erTotalTime)))
                {
                    //std::cout << "opt2 valable" << std::endl;
                    vtSolution.validateTournee(ir->second);
                    vtSolution.validateTournee(it->second);
                    //std::cout << "opt2 end trait" << std::endl;
                }
                else
                {
                    //std::cout << "opt2 non valable" << std::endl;
                    vtSolution.restoreTournee(ir->second);
                    vtSolution.restoreTournee(it->second);
                    //std::cout << "opt2 non valable end trait" << std::endl;
                    continue;
                }
                
            }
            
            // evaluation de la solution
            vtSolution.eval(&soleval);
            
            // si amelioration
            if (soleval < solevalbest)
            {
                last_amelioration_iteration = iteration;
                amelioration++;
                vtBestSolution = vtSolution;
                solevalbest = soleval;
                
                //vtSolution = vtBestSolution;
                //soleval = solevalbest;
                
                //std::cout << vtBestSolution.toString() << std::endl;
                //std::cout << solevalbest.toString() << std::endl;
                //std::cout << "amelioration " << amelioration << "@" << iteration << std::endl;
            }
            
            if (iteration - last_amelioration_iteration > inMaxIt)
                break;
        }
    }
    EXC_RETHROW_END()
}
